package com.study.web.aop;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
//import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//import org.springframework.core.annotation.Order;
//import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;

import com.alibaba.fastjson.JSON;
import com.study.common.exception.BizException;
import com.study.common.utils.RequestValidateUtil;
import com.study.common.utils.ResponseUtil;

/**
 * 系统功能：对请求的实体参数进行校验<br>
 * RequestParamsValidateAop.java
 * 
 * @author yaojinshan
 * @since 2018年6月14日上午10:10:56
 */
//@Aspect
//@Order(1)
//@Component
public class RequestParamsValidateAop {
	
	static Logger logger = LoggerFactory.getLogger(RequestParamsValidateAop.class);
	//所有拥有该注解的方法 会调用该方法
	@Pointcut("@annotation(com.study.web.annotation.RequestParamNeedValidateAnnotation)")
	public void requestParamNeedValidate() {
	
	}
	
	@Around("requestParamNeedValidate()")
	public Object validateParams(ProceedingJoinPoint joinPoint) throws Throwable{
		String execName = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName();//当前执行方法名称
		
		Object[] args = joinPoint.getArgs();
		String msg = "";
		if(args.length < 3){
			msg = "当前方法：" + execName + "不适用注解@RequestParamNeedValidateAnnotation,或者参数格式不正确。"; 
			throw new BizException(msg);
		}
		Class<?>[] clazzArray = new Class<?>[args.length];
		// A.class.isAssignableFrom(B.class) : 判断A和 B是否是同一个类，或者A类是否是B类的超类或超接口类。
		// obj instance Class ： 判断obj对象 是否是 Class类或接口或父类或父接口的一个实例对象。
		for (int i = 0; i < args.length; i++) {
			if(args[i] instanceof HttpServletRequest){
//			if(HttpServletRequest.class.isAssignableFrom(args[i].getClass())){
				clazzArray[i] = HttpServletRequest.class;
				continue;
			}else if(args[i] instanceof HttpServletResponse){
//			}else if(HttpServletResponse.class.isAssignableFrom(args[i].getClass())){
				clazzArray[i] = HttpServletResponse.class;
				continue;
//			}else if(BindingResult.class.isAssignableFrom(args[i].getClass())){
			}else if(args[i] instanceof BindingResult){
				clazzArray[i] = BindingResult.class;
				continue;
			}else{
				clazzArray[i] = args[i].getClass();
			}
		}
		Method method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), clazzArray);//获取方法
		List<Integer> validAnnoCount = new ArrayList<Integer>();//用于保存 使用@Valid注解的参数的位置
		Parameter[] parameters = method.getParameters();//获取方法的所有参数
		for (int i = 0; i < parameters.length; i++) {
			//记录参数列表中 使用@Valid注解 的参数 的位置
			Valid validAnnotation = parameters[i].getAnnotation(javax.validation.Valid.class);
			if(validAnnotation != null){
				validAnnoCount.add(i);
			}
		}
		HttpServletResponse response = (HttpServletResponse) args[0];
		//记录所有的错误
		for(int paramIndex : validAnnoCount){
			if(args[paramIndex+1] instanceof BindingResult){
				BindingResult result =  (BindingResult) args[paramIndex+1];
				msg += RequestValidateUtil.getErrorMsgString(result);
			}else{
				msg = "当前方法：" + execName + "第" + (paramIndex + 1) +"个参数，注解@Valid 和 BindingResult必须成对出现!"; 
				throw new BizException(msg);
			}
		}
		if(StringUtils.isNotEmpty(msg)){
			msg = msg.substring(0, msg.length() - 1);
			response.setContentType("application/json;charset=utf-8");
			String result = ResponseUtil.RES_FAIL;
	    	Map<String, Object> resultMap = new HashMap<String, Object>();
	    	resultMap.put("msg", msg);
    		resultMap.put("result", result);
			response.getWriter().print(JSON.toJSONString(resultMap));
			return null;
		}else{
			Object result = joinPoint.proceed();
			return result;
		}
	}
}
